package com.crowntit.domain.trade.service.settlement;

import com.alibaba.fastjson.JSON;
import com.crowntit.domain.trade.adapter.port.ITradePort;
import com.crowntit.domain.trade.adapter.repository.ITradeRepository;
import com.crowntit.domain.trade.model.aggregate.TradeOrderSettlementAggregate;
import com.crowntit.domain.trade.model.entity.*;
import com.crowntit.domain.trade.model.valobj.GroupBuyTeamStatusVO;
import com.crowntit.domain.trade.model.valobj.TradeOrderStatusEnumVO;
import com.crowntit.domain.trade.service.ITradeOrderSettlementService;
import com.crowntit.domain.trade.service.settlement.factory.TradeSettlementRuleFilterFactory;
import com.crowntit.types.design.framework.link.model2.chain.BusinessLinkedLink;
import com.crowntit.types.enums.NotifyTaskHTTPEnumVO;
import com.crowntit.types.enums.ResponseCode;
import com.crowntit.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class TradeOrderSettlementService implements ITradeOrderSettlementService {

    @Resource
    private ITradePort tradePort;

    @Resource
    private ITradeRepository repository;

    @Resource(name = "tradeSettlementRuleFilterLink")
    private BusinessLinkedLink<TradeSettlementRuleCommandEntity, TradeSettlementRuleFilterFactory.DynamicContext, TradeSettlementRuleBackEntity> tradeSettlementRuleFilterLink;

    @Override
    public TradeOrderSettlementEntity settlementMarketPayOrder(TradeOrderPaySuccessEntity tradeOrderPaySuccessEntity) throws Exception {
        // 参数校验
        String source = tradeOrderPaySuccessEntity.getSource();
        String channel = tradeOrderPaySuccessEntity.getChannel();
        String userId = tradeOrderPaySuccessEntity.getUserId();
        String outTradeNo = tradeOrderPaySuccessEntity.getOutTradeNo();

        TradeSettlementRuleBackEntity ruleBackEntity = tradeSettlementRuleFilterLink.apply(TradeSettlementRuleCommandEntity.builder()
                        .userId(userId)
                        .outTradeNo(outTradeNo)
                        .source(source)
                        .channel(channel)
                        .build(),
                new TradeSettlementRuleFilterFactory.DynamicContext());

        GroupBuyTeamEntity groupBuyTeamEntity = GroupBuyTeamEntity.builder()
                .activityId(ruleBackEntity.getActivityId())
                .status(ruleBackEntity.getStatus())
                .notifyEntity(ruleBackEntity.getNotifyEntity())
                .completeCount(ruleBackEntity.getCompleteCount())
                .targetCount(ruleBackEntity.getTargetCount())
                .lockCount(ruleBackEntity.getLockCount())
                .teamId(ruleBackEntity.getTeamId())
                .source(ruleBackEntity.getSource())
                .channel(ruleBackEntity.getChannel())
                .validStartTime(ruleBackEntity.getValidStartTime())
                .validEndTime(ruleBackEntity.getValidEndTime())
                .build();

        // 构建入参
        TradeOrderSettlementAggregate tradeOrderSettlementAggregate = TradeOrderSettlementAggregate.builder()
                .userEntity(UserEntity.builder()
                        .userId(userId)
                        .build())
                .teamEntity(groupBuyTeamEntity)
                .tradeOrderPaySuccessEntity(tradeOrderPaySuccessEntity)
                .build();

        // 更新数据库
        boolean isTeamComplete = repository.settlementMarketPayOrder(tradeOrderSettlementAggregate);
        GroupBuyTeamStatusVO groupBuyTeamStatusVO = isTeamComplete ? GroupBuyTeamStatusVO.COMPLETE : GroupBuyTeamStatusVO.WAITING;

        if (isTeamComplete){
            Map<String, Integer> result = execSettlementNotifyJob(groupBuyTeamEntity.getTeamId());
            log.info("拼团交易结算-检测到拼团完成，执行回调任务：{}", JSON.toJSONString(result));
        }

        TradeOrderSettlementEntity tradeOrderSettlementEntity = TradeOrderSettlementEntity.builder()
                .source(source)
                .channel(channel)
                .activityId(groupBuyTeamEntity.getActivityId())
                .userId(userId)
                .teamId(groupBuyTeamEntity.getTeamId())
                .outTradeNo(outTradeNo)
                .orderStatus(TradeOrderStatusEnumVO.COMPLETE.getCode())
                .groupBuyTeamStatus(groupBuyTeamStatusVO.getCode())
                .build();

        // 返回数据
        return tradeOrderSettlementEntity;
    }

    @Override
    public Map<String, Integer> execSettlementNotifyJob(String teamId) throws Exception {
        log.info("拼团交易-执行结算通知回调，指定 teamId:{}", teamId);
        List<NotifyTaskEntity> notifyTaskEntities = repository.queryUnExecutedNotifyTaskByTeamId(teamId);
        return execSettlementNotifyJob(notifyTaskEntities);
    }

    @Override
    public Map<String, Integer> execSettlementNotifyJob() throws Exception {
        log.info("拼团交易-执行结算通知任务");
        List<NotifyTaskEntity> notifyTaskEntities = repository.queryUnExecutedNotifyTaskList();
        return execSettlementNotifyJob(notifyTaskEntities);
    }

    private Map<String, Integer> execSettlementNotifyJob(List<NotifyTaskEntity> taskEntityList) throws Exception {
        int successCount = 0, errorCount = 0, retryCount = 0;
        for (NotifyTaskEntity notifyTaskEntity : taskEntityList) {
            String response = tradePort.groupBuyNotify(notifyTaskEntity);

            if (response.equals(NotifyTaskHTTPEnumVO.SUCCESS.getCode())){
                int updatedCount = repository.updateNotifyTaskStatus2SUCCESS(notifyTaskEntity.getTeamId());
                if (updatedCount != 1){
                    throw new Exception(ResponseCode.UPDATE_ZERO.getInfo());
                } else {
                    successCount += 1;
                }
            } else if (response.equals(NotifyTaskHTTPEnumVO.ERROR.getCode())){
                if (notifyTaskEntity.getNotifyCount() < 5){
                    int updatedCount = repository.updateNotifyTaskStatus2ERROR(notifyTaskEntity.getTeamId());
                    if (updatedCount != 1){
                        throw new Exception(ResponseCode.UPDATE_ZERO.getInfo());
                    } else {
                        errorCount += 1;
                    }
                } else {
                    int updatedCount = repository.updateNotifyTaskStatus2RETRY(notifyTaskEntity.getTeamId());
                    if (updatedCount != 1){
                        throw new Exception(ResponseCode.UPDATE_ZERO.getInfo());
                    } else {
                        retryCount += 1;
                    }
                }
            }
        }

        Map<String, Integer> map = new HashMap<>();
        map.put("waitCount", taskEntityList.size());
        map.put("successCount", successCount);
        map.put("errorCount", errorCount);
        map.put("retryCount", retryCount);
        return map;
    }
}
